package com.iltgcl.echovoicedemo.speaker

import android.app.Application
import android.content.Context
import android.content.SharedPreferences
import android.media.AudioManager
import android.speech.tts.TextToSpeech
import android.speech.tts.UtteranceProgressListener
import android.util.SparseArray
import androidx.core.util.forEach
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.iltgcl.echovoice.client.EchoVoiceUtils
import com.iltgcl.echovoice.client.IEchoVoice
import com.iltgcl.echovoice.di.ApplicationScope
import com.iltgcl.echovoicedemo.R
import com.iltgcl.echovoicedemo.data.SettingsRepository
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.launch
import timber.log.Timber
import javax.inject.Inject
import javax.inject.Singleton

@OptIn(ExperimentalCoroutinesApi::class)
@Singleton
class SpeakerManager @Inject constructor(
    private val application: Application,
    @ApplicationScope private val applicationScope: CoroutineScope,
    private val settingsRepository: SettingsRepository) {
    private val audioManager = application.getSystemService(Context.AUDIO_SERVICE) as AudioManager
    private var echoVoice: IEchoVoice? = null
    private val speakers = SparseArray<IEchoSpeaker>()

    val initialize = MutableLiveData<Boolean>()

    fun getSpeakerState(usage: Int): LiveData<Int>? {
        return speakers.get(usage)?.speakerState
    }

    fun getRangeStart(usage: Int): LiveData<RangeStartInfo>? {
        return speakers.get(usage)?.rangeStart
    }

    private val utteranceProgressListener = object : UtteranceProgressListener() {
        override fun onStart(utteranceId: String?) {
            Timber.d("[onStart]$utteranceId")
            applicationScope.launch(Dispatchers.Main) {
                utteranceId?.let {
                    speakers.forEach { _, speaker -> speaker.onSpeechStart(it) }
                }
            }
        }

        override fun onStop(utteranceId: String?, interrupted: Boolean) {
            Timber.d("[onStop]$utteranceId")
            super.onStop(utteranceId, interrupted)
            applicationScope.launch(Dispatchers.Main) {
                utteranceId?.let {
                    speakers.forEach { _, speaker ->  speaker.onSpeechStop(it) }
                }
            }
        }

        override fun onDone(utteranceId: String?) {
            Timber.d("[onDone]$utteranceId")
            applicationScope.launch(Dispatchers.Main) {
                utteranceId?.let {
                    speakers.forEach { _, speaker -> speaker.onSpeechDone(it) }
                }
            }
        }

        override fun onError(utteranceId: String?) {
            Timber.d("[onError]$utteranceId")
            applicationScope.launch(Dispatchers.Main) {
                utteranceId?.let {
                    speakers.forEach { _, speaker -> speaker.onSpeechError(it) }
                }
            }
        }

        override fun onRangeStart(utteranceId: String?, start: Int, end: Int, frame: Int) {
            Timber.d("[onRangeStart]@@@ $utteranceId, start:$start, end:$end")
            applicationScope.launch(Dispatchers.Main) {
                utteranceId?.let {
                    val rangeStartInfo = RangeStartInfo(it, start, end, frame)
                    speakers.forEach { _, speaker -> speaker.onRangeStart(rangeStartInfo) }
                }
            }
        }
    }

    init {
        Timber.d("------------ init!!")
        echoVoice = initTts()
    }

    fun speak(usage: Int,
              text: CharSequence,
              utteranceId: String,
              voiceName:Int = -1,
              speed: Int = -1,
              volume:Int = -1,
              pitch:Int = -1) =
        speakers.get(usage)?.speak(text, utteranceId, voiceName, speed, volume, pitch)


    fun stop(usage: Int)= speakers.get(usage)?.stop()
    fun pause(usage: Int) = speakers.get(usage)?.pause()
    fun resume(usage: Int) = speakers.get(usage)?.resume()

    private fun initSpeakers() {
        echoVoice?.let {
            speakers.put(IEchoVoice.USAGE_MEDIA, MediaSpeaker(audioManager, it, settingsRepository.getMediaFocusLossPolicy()))
            speakers.put(IEchoVoice.USAGE_NOTIFICATION, NavigationSpeaker(audioManager, it))
            speakers.put(IEchoVoice.USAGE_A11Y, A11ySpeaker(audioManager, it))
            settingsRepository.registerOnSharedPreferenceChangeListener(onSharedPreferencesChangedListener)
        }
    }

    private val onSharedPreferencesChangedListener = SharedPreferences.OnSharedPreferenceChangeListener { _, key ->
        when(key) {
            application.getString(R.string.pref_media_focus_loss_policy_key) -> {
                speakers.get(IEchoVoice.USAGE_MEDIA)?.let {
                    val mediaFocusLossPolicy = settingsRepository.getMediaFocusLossPolicy()
                    it.focusLossTransientPolicy = mediaFocusLossPolicy
                }
            }
            else -> {}
        }
    }

    private fun initTts():IEchoVoice? {
        return EchoVoiceUtils.createAIDLEchoVoice(application, { status ->
            when (status) {
                TextToSpeech.SUCCESS -> {
                    initSpeakers()
                    initialize.postValue(true)
                    Timber.d("######## TTS Init Success!")
                }
                else -> {
                    initialize.postValue(false)
                    Timber.e("######## TTS Init Fail!")
                }
            }
        }, utteranceProgressListener)
    }


}